<?php

namespace app\http;

use app\biz\model\Content;
use think\facade\Cache;
use think\worker\Server;
use utils\LogUtils;
use Workerman\Lib\Timer;


class Worker extends Server
{
     //四川保健医院 2346
    protected $socket = 'websocket://0.0.0.0:2346';
    //进程数量  window 下不管用
    //protected $option = ['count' => 4];


    protected $option = ['transport' => 'ssl', 'count' => 4];
    protected $context = [
        'ssl' => [
            //'local_cert' => 'C:\phpstudy_pro\zs\test.kyd5.cn_bundle.pem',
            //'local_pk' => 'C:\phpstudy_pro\zs\test.kyd5.cn.key',
            //本地测试
            //'local_cert'=>'D:\phpstudy_pro\WWW\take-num\zs\local_pem.pem',
            //'local_pk'=>'D:\phpstudy_pro\WWW\take-num\zs\local_key.key',
            'verify_peer' => false,
            'verify_peer_name' => false,
            'allow_self_signed' => true,
            'protocols'=>'TLSv1.1 TLSv1.2'
        ],
    ];

    public $conn_list01 = [];
    public $conn_list02 = [];

    public $conn_list03 = [];

    public $conn_time = [];


    public $timeout_num = 5 * 60;
    //public $timeout_num = 10;

    public $map_time = [];

    public function __construct()
    {
        if (!\MyConfig::$onLine) {
            unset($this->option['transport']);
        }
        //$this->context['ssl']['local_cert'] = getFileROOT() . '\zs\ws.kyd5.cn_bundle.pem';
        //$this->context['ssl']['local_pk'] = getFileROOT() . '\zs\ws.kyd5.cn.key';
        $this->context['ssl']['local_cert'] = getFileROOT() . '\zs\kyd5.cn.pem';
        $this->context['ssl']['local_pk'] = getFileROOT() . '\zs\kyd5.cn.key';
        parent::__construct();
    }

    protected function init()
    {
        parent::init();
    }

    public function onWorkerStart()
    {

        Timer::add(60, function () {
            //清除无效连接
            if (count($this->worker->connections)>0){
                foreach ($this->worker->connections as $connection) {
                    //没有找到
                    if (!$this->isYXConn($connection->id)){
                        LogUtils::log("有无效连接,清除:".$connection->id);
                        $connection->close();
                    }
                }
            }
        });


        //定时任务
        Timer::add(1, function () {
            //叫号数据
            $num_data_array = $this->getDBData(1);

            $this->exTask($num_data_array);

            //打印数据
            $print_data_array =  $this->getDBData(2);
            $this->exTask_print($print_data_array);

            //聊天数据
            $chat_data_array = $this->getDBData(3);

            $this->exTask_chat($chat_data_array);

            //给app 发消息
            $app_array = $this->getDBData(5);
            $this->exTask_app_data($app_array);
        });

        //群发消息
        Timer::add(1, function () {

            $d =  Content::where("tab", 4)->find();
            if ($d != null) {
                $bean = json_decode($d->content, true);
                $array = $bean['data'];

                $data = ['siteid' => $array['siteid'], 'member_id' => $array['member_id'], 'tpl_id' => $array['tpl_id']];

                $result = postRequest($array['url'], $data, true, true);
                $r_str= json_encode($result, JSON_UNESCAPED_UNICODE);
                LogUtils::log('群发返回结果'.$r_str);
                //成功或者失败三次了
                if (($result && isset($result['code']) && $result['code'] == 200)) {
                    // 删除指定索引位置的元素
                    //array_splice($array, 0, 1);
                    Content::destroy($d['id']);
                }else{
                    if (time() - $d['data']['mytime'] >= 4) {
                        $su = json_encode($bean, JSON_UNESCAPED_UNICODE);
                        LogUtils::error("群发 时间超过设定=".$su);
                        Content::destroy($d['id']);
                    }
                }
            }
        });
    }


    /**
     * 发送 叫号数据
     */
    private function exTask($array)
    {
        foreach ($array as $value) {
            //LogUtils::error('设备=='.$Device_ID);
            $Device_ID = $value['data']['From'];
            $data = $value['data'];

            $su = json_encode($data, JSON_UNESCAPED_UNICODE);

            //判断是否在线,
            if (!$this->isDeviceConn($Device_ID)) {
                //判断时间, 如果太长, 就删除数据
                if (time() - $value['data']['mytime'] >= $this->timeout_num) {

                    LogUtils::error("num 时间超过设定=".$su);
                    Content::destroy($value['data']['id']);
                }
                continue;
            }

            //$this->setMessage2AllDevice002(1,0, $Device_ID, $su);
            $this->setMessage2Device05(2,0,0, $Device_ID,0, $su);
            //数据库删除
            Content::destroy($value['data']['id']);
        }

    }

    /**
     * @param $array
     */
    private function exTask_print($array)
    {
        //LogUtils::error($array);
        foreach ($array as $value) {
            $Device_ID = $value['data']['From'];
            //判断是在线
            $data = $value['data'];

            $Websocket = isset($data['Websocket']) ? $data['Websocket'] : '';

            $isConn01 = empty($Websocket) && $this->isDeviceConn($Device_ID);

            $isConn02 = !empty($Websocket) && $this->isDeviceConn02($Device_ID, $Websocket);

            $su = json_encode($data, JSON_UNESCAPED_UNICODE);

            //判断是否在线
            if (!$isConn01 && !$isConn02) {
                if (time() - $value['data']['mytime'] >= $this->timeout_num) {

                    LogUtils::error("print 时间超过设定=".$su);
                    Content::destroy($value['data']['id']);
                }
                continue;
            }
            //打印

            //没有传递, $Websocket
            if ($isConn01) {
                //给所有的发
                $this->setMessage2AllDevice03($Device_ID, $su);
            } else if ($isConn02) {
                //单独发
                //$this->setMessage2Sigle(1,0, $Device_ID, $Websocket, $su);
                $this->setMessage2Device05(1,0,0, $Device_ID, $Websocket, $su);

            }
            //删除数据
            Content::destroy($value['data']['id']);
        }

    }

    private function exTask_chat($array)
    {
        foreach ($array as $value) {

            $Device_ID = $value['data']['From'];
            $data = $value['data'];

            $member = isset($data['member']) ? $data['member'] : '';
            $memberid = isset($data['memberid']) ? $data['memberid'] : '';

            //客服是否在线
            $kf_is_conn = $this->isDeviceConn02($Device_ID, $Device_ID);
            //要接收消息的对方是否在线
            $member_is_conn = $this->isDeviceConn02($Device_ID, $member);
            $memberId_is_conn = $this->isDeviceConn02($Device_ID, $memberid);

            //并且接受消息的对象不是客服, 如果接受消息的对象是客服, 在 代码注释999 处会发
            $isNotKf = $Device_ID != $member && $Device_ID != $memberid;

            $su = json_encode($data, JSON_UNESCAPED_UNICODE);
            //LogUtils::error($Device_ID);
            //判断是否在线
            if (!$kf_is_conn && !$member_is_conn && !$memberId_is_conn) {
                //并且数据数据间隔很大了
                if (time() - $value['data']['mytime'] >= $this->timeout_num) {
                    //数据清空
                    LogUtils::error("chat 时间超过设定=".$su);
                    Content::destroy($value['data']['id']);
                }
                continue;
            }


            //$su = json_encode($data, JSON_UNESCAPED_UNICODE);

            //设备是否连接
            if ($member_is_conn) {
                //$this->setMessage2Sigle(1,0, $Device_ID, $member, $su);
                $this->setMessage2Device05(1,0,0, $Device_ID, $member, $su);
            }

            if ($memberId_is_conn && $member != $memberid) {
                $this->setMessage2Device05(1,0,0, $Device_ID, $memberid, $su);
            }

            if ($kf_is_conn && $isNotKf) {
                $this->setMessage2Device05(1,0,0, $Device_ID, $Device_ID, $su);
            }

            Content::destroy($value['data']['id']);
        }

    }

    /**
     * 给app 发小心
     */
    public function exTask_app_data($array){

        foreach ($array as $value) {

            $Device_ID = $value['data']['From'];
            $data = $value['data'];

            if (!isset($data['Type'])){
                $data['Type']= 1;
            }
            //LogUtils::error($data);
            $su = json_encode($data, JSON_UNESCAPED_UNICODE);
            switch ($data['Type']){
                case 1:
                    //单发
                    if (!$this->isDeviceConn03(3,$Device_ID,$data['Websocket'])){
                        if (time() - $value['data']['mytime'] >= $this->timeout_num) {
                            //数据清空
                            LogUtils::error("app_data 时间超过设定=".$su);
                            Content::destroy($value['data']['id']);
                        }
                    }else{
                        //$type 1,2
                        $this->setMessage2Device05($data['Type'],0,3, $Device_ID,$data['Websocket'],$su);
                        Content::destroy($value['data']['id']);
                    }

                    break;
                case 2:
                    //多发
                    if (!$this->isDeviceConn03(3,$Device_ID,-1)){
                        if (time() - $value['data']['mytime'] >= $this->timeout_num) {
                            //数据清空
                            LogUtils::error("app_data(多发) 时间超过设定=".$su);
                            Content::destroy($value['data']['id']);
                        }
                    }else{
                        //$type 1,2
                        $this->setMessage2Device05($data['Type'],0,3, $Device_ID,$data['Websocket'],$su);
                        Content::destroy($value['data']['id']);
                    }
                    break;
            }
        }

    }


    public function onMessage($connection, $d)
    {

        if (empty($d)) {
            $su = $this->fail02(403, '接受到的数据为空');
            $connection->send($su);
            return;
        }

        $endStr = substr($d, -1);
        if ($endStr != "}") {
            LogUtils::log('数据格式错误');
            return;
        }
        LogUtils::log($d);
        $data = json_decode($d);
        $Code = isset($data->Code) ? intval($data->Code) : -1;

        if ($Code == 404 || $Code == 403) {
            //不要再回消息了
            return;
        }

        if (!isset($data->From)) {
            //$this->setErrorMessage($connection, 'Device_ID 空,接受到的信息=' . $d);
            $su = $this->fail02(404, 'From 空,接受到的信息=');
            $connection->send($su);
            return;
        }

        $From = intval(isset($data->From) ? $data->From : '0');
        $Device_ID = strval(isset($data->Device_ID) ? $data->Device_ID : "");
        $Websocket = strval(isset($data->Websocket) ? $data->Websocket : "");
        $back_code = isset($data->back_code) ? $data->back_code : 0;

        //记录连接时间
        $this->conn_time[$Device_ID] = time();

        switch ($Code) {
            case 0:
                $msg = '接受 $Device_ID:' . $Device_ID . " From:" . $data->From;
                $key = $Device_ID . '@@@@' . $Websocket;

                if ($From == 1) {

                } else if ($data->From == 3) {
                    $this->clearMyListAndConnId($this->conn_list03, $key);
                    $this->conn_list03[$key] = $connection->id;
                } else {
                    $this->clearMyListAndConnId($this->conn_list02, $key);
                    $this->conn_list02[$key] = $connection->id;

                }
                $connection->send($this->ok2($msg));
                break;

            case 10:
                //客户端心跳包(打印的会发送)
                // 获取当前时间
                $current_time = date('Y-m-d H:i:s');
                $su = $this->ok3(10, $current_time);
                $connection->send($su);

                break;
            case 999:
                //$a = 3/0;
                break;
            case -1:

            default:

                break;
        }
        switch ($back_code) {
            case 0:

                break;
            case 21:
                //num
                $this->setDataBack(21, $data);
                break;
            case 22:
                //print
                $this->setDataBack(22, $data);
                break;
            case 23:
                //chat
                $this->setDataBack(23, $data);
                break;
        }
    }


    public function ok($Data)
    {
        $su = ['Code' => 200, 'Data' => $Data, 'message' => 'success'];
        $su = json_encode($su, JSON_UNESCAPED_UNICODE);
        return $su;
    }

    public function ok2($Message)
    {
        $su = ['Code' => 200, 'Data' => null, 'message' => $Message];
        $su = json_encode($su, JSON_UNESCAPED_UNICODE);
        return $su;
    }

    public function ok3($Code, $Data)
    {
        $su = ['Code' => $Code, 'Data' => $Data, 'message' => null];
        $su = json_encode($su, JSON_UNESCAPED_UNICODE);
        return $su;
    }

    public function fail($Message)
    {
        $su = ['Code' => 400, 'Data' => null, 'message' => $Message];
        $su = json_encode($su, JSON_UNESCAPED_UNICODE);
        return $su;
    }

    public function fail02($Code, $Message)
    {
        $su = ['Code' => $Code, 'Data' => null, 'message' => $Message];
        $su = json_encode($su, JSON_UNESCAPED_UNICODE);
        return $su;
    }


    /**
     * 当连接建立时触发的回调函数
     * @param $connection
     */
    public function onConnect($connection)
    {
        $connection->send($this->ok2('success,id=' . $connection->id));
    }

    /**
     * 当连接断开时触发的回调函数
     * @param $connection
     */
    public function onClose($connection)
    {
        foreach ($this->conn_list01 as $key => $value) {
            if ($connection->id == $value) {
                LogUtils::error('close conn_list01=' . $key . '==' . $value);
                unset($this->conn_list01[$key]);
                break;
            }
        }
        foreach ($this->conn_list02 as $key => $value) {
            if ($connection->id == $value) {
                LogUtils::error('close conn_list02=' . $key . '==' . $value);
                unset($this->conn_list02[$key]);
                break;
            }
        }

        foreach ($this->conn_list03 as $key => $value) {
            if ($connection->id == $value) {
                LogUtils::error('close conn_list03=' . $key . '==' . $value);
                unset($this->conn_list03[$key]);
                break;
            }
        }

        //LogUtils::log("onClose");
        $this->clear($connection);

    }

    /**
     * 当客户端的连接上发生错误时触发
     * @param $connection
     * @param $code
     * @param $msg
     */
    public function onError($connection, $code, $msg)
    {
        LogUtils::error('worker onError:' . $code . '=' . $msg);
    }



    /**
     * 不给, 连接者Websocket ==$Device_ID的连接发 就是客服
     *
     * @param $Device_ID
     * @param $d
     * @return bool|void
     */
    protected function setMessage2AllDevice03($Device_ID, $d)
    {

        //给所有都发, 不给 连接者Websocket ==$Device_ID的连接发
        foreach ($this->conn_list02 as $key => $value) {
            // $key = 999@@@@0 , 999@@@@1
            $array = explode('@@@@', $key);

            //本身
            //$array[1] != $Device_ID 连接者的Websocket 和 他本身的 $Device_ID 是一样的,
            $ok = count($array) == 1 || (!empty($array[1]) && $array[1] != $Device_ID);

            if ($array[0] == $Device_ID && $ok) {
                //池子里面连接存在
                if (isset($this->worker->connections[$value])) {
                    $conn = $this->worker->connections[$value];
                    $conn->send($d);
                }
            }
        }
    }


    /**
     * 发送消息给连接的设备
     * $type 单发/群发
     * $from 来自哪里
     * $To 发给谁
     * $Device_ID 大厅号码
     * $Websocket 设备序号
     * $d 要发送的信息
     */
    protected function setMessage2Device05($type,$from, $To, $Device_ID,$Websocket,$d)
    {
        $isSend = false;

        $arrayAll = [];
        if ($To == 0) {
            $arrayAll = $this->conn_list02;
        }

        if ($To == 3) {
            $arrayAll = $this->conn_list03;
        }

        switch ($type){
            case 1:
                //单发
                $str = $Device_ID . '@@@@' . $Websocket;
                foreach ($arrayAll as $key => $value) {
                    if ($key == $str) {
                        //池子里面连接存在
                        if (isset($this->worker->connections[$value])) {
                            $conn = $this->worker->connections[$value];
                            $conn->send($d);
                            $isSend = true;
                        }
                    }
                }
                break;
            case 2:
                //多发
                foreach ($arrayAll as $key => $value) {
                    // $key = 999@@@@0 , 999@@@@1
                    $array = explode('@@@@', $key);

                    if ($array[0] == $Device_ID) {
                        //池子里面连接存在
                        if (isset($this->worker->connections[$value])) {
                            $conn = $this->worker->connections[$value];
                            $conn->send($d);
                            $isSend = true;
                        }
                    }
                }
                break;
        }

        return $isSend;
    }


    protected function isDeviceConn($Device_ID)
    {
        foreach ($this->conn_list02 as $key => $value) {
            // $key = 999@@@@0 , 999@@@@1
            $array = explode('@@@@', $key);

            if ($array[0] == $Device_ID) {
                //池子里面连接存在
                if (isset($this->worker->connections[$value])) {
                    return true;
                }
            }
        }
        return false;
    }

    protected function isDeviceConn02($Device_ID, $Websocket)
    {
        foreach ($this->conn_list02 as $key => $value) {
            // $key = 999@@@@0 , 999@@@@1
            $str = $Device_ID . '@@@@' . $Websocket;

            if ($key == $str) {
                //池子里面连接存在
                if (isset($this->worker->connections[$value])) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 判读设备是否连接
     * @param $from
     * @param $Device_ID
     * @param $Websocket
     * @return bool
     */
    protected function isDeviceConn03($To, $Device_ID, $Websocket)
    {

        $arrayAll = [];
        if ($To == 0) {
            $arrayAll = $this->conn_list02;
        }

        if ($To == 3) {
            $arrayAll = $this->conn_list03;
        }
        foreach ($arrayAll as $key => $value) {
            // $key = 999@@@@0 , 999@@@@1
            $array = explode('@@@@', $key);

            if ($Websocket < 0) {
                //一堆连接是否存在
                if ($array[0] == $Device_ID) {
                    //池子里面连接存在
                    if (isset($this->worker->connections[$value])) {
                        return true;
                    }
                }
            } else {
                //一对一连接是否存在
                if ($array[0] == $Device_ID && $array[1] == $Websocket) {
                    if (isset($this->worker->connections[$value])) {
                        return true;
                    }
                }
            }
        }
        return false;
    }


    /**
     * @param $connection
     */
    private function clear($connection)
    {
        if (isset($connection->id)) {
            // 连接断开时删除映射
            unset($this->worker->connections[$connection->id]);
        }
    }

    private function clearMyListAndConnId($array, $key)
    {
        if (isset($array[$key])) {
            $id = $array[$key];

            //可能已经提前断开了, 要判断一下
            if (isset($this->worker->connections[$id])) {
                LogUtils::error("强行断开".$key);
                $this->worker->connections[$id]->close();
            }

            //$this->onClose($this->worker->connections[$id]);
            unset($array[$key]);
        }
    }


    /**
     * 数据返回以后
     * @param $type
     * @param $data
     */
    private function setDataBack($type, $back)
    {

        if ($type == 21) {

        }

        if ($type == 22) {

        }

        if ($type == 23) {

        }
    }

    /**
     * 是否可用连接
     */
    private function isYXConn($id){
        foreach ($this->conn_list02 as $key => $value) {
            if ($id == $value){
                return true;
            }
        }

        foreach ($this->conn_list03 as $key => $value) {
            if ($id == $value){
                return true;
            }
        }
        return false;
    }

    private function getDBData($tab){
        $array = [];
        $list = Content::where("tab", $tab)->select();

        if ($list && count($list) > 0) {
            foreach ($list as $item) {
                $valueBean = json_decode($item->content, true);
                //存储id, 为了发送完毕删除数据
                $valueBean['data']['id'] = $item->id;
                $Device_ID = $valueBean['data']['From'];
                //连接着, 如果中间段了, 那就彻底放弃了
                //LogUtils::error($item);
                array_push($array, $valueBean);
            }
        }
        return $array;
    }

}